package com.iotechn.unimall.app.api.spuUser.user;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.iotechn.unimall.core.Const;
import com.iotechn.unimall.core.exception.AppServiceException;
import com.iotechn.unimall.core.exception.ExceptionDefinition;
import com.iotechn.unimall.core.exception.ServiceException;
import com.iotechn.unimall.core.notify.SMSClient;
import com.iotechn.unimall.core.notify.SMSResult;
import com.iotechn.unimall.core.util.GeneratorUtil;
import com.iotechn.unimall.data.component.CacheComponent;
import com.iotechn.unimall.data.domain.SpuUserDO;
import com.iotechn.unimall.data.domain.UserShopDO;
import com.iotechn.unimall.data.dto.SpuUserDTO;
import com.iotechn.unimall.data.enums.UserLoginType;
import com.iotechn.unimall.data.mapper.SpuUserMapper;
import com.iotechn.unimall.data.mapper.UserShopMapper;
import com.iotechn.unimall.data.util.SessionUtil;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.codec.digest.Md5Crypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;

/**
 * Created by rize on 2019/6/30.
 */
@Service
public class SpuUserServiceImpl implements SpuUserService {

    private static final String VERIFY_CODE_PREFIX = "VERIFY_CODE_";

    private static final Logger logger = LoggerFactory.getLogger(SpuUserServiceImpl.class);

    @Autowired
    private SpuUserMapper userMapper;
    @Autowired
    private UserShopMapper userShopMapper;

    @Autowired
    private SMSClient smsClient;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private StringRedisTemplate userRedisTemplate;

    private OkHttpClient okHttpClient = new OkHttpClient();

    @Value("${com.iotechn.unimall.wx.mini.app-id}")
    private String wxMiNiAppid;

    @Value("${com.iotechn.unimall.wx.mini.app-secret}")
    private String wxMiNiSecret;

    @Value("${com.iotechn.unimall.wx.app.app-id}")
    private String wxAppAppid;

    @Value("${com.iotechn.unimall.wx.app.app-secret}")
    private String wxAppSecret;

    @Value("${com.iotechn.unimall.wx.h5.app-id}")
    private String wxH5Appid;

    @Value("${com.iotechn.unimall.wx.h5.app-secret}")
    private String wxH5Secret;

    @Override
    public String sendVerifyCode(String phone) throws ServiceException {
        String verifyCode = GeneratorUtil.genSixVerifyCode();
        SMSResult res = smsClient.sendRegisterVerify(phone, verifyCode);
        if (res.isSucc()) {
            cacheComponent.putRaw(VERIFY_CODE_PREFIX + phone, verifyCode, 300);
            return "ok";
        } else {
            throw new AppServiceException(res.getMsg(), ExceptionDefinition.USER_SEND_VERIFY_FAILED.getCode());
        }

    }

    @Override
    @Transactional
    public String register(String phone, String password, String verifyCode, String ip) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        Integer count = userMapper.selectCount(
                new EntityWrapper<SpuUserDO>()
                        .eq("phone", phone));
        if (count > 0) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_HAS_EXISTED);
        }
        //3.校验成功，注册用户
        Date now = new Date();
        SpuUserDO userDO = new SpuUserDO();
        userDO.setPhone(phone);
        userDO.setPassword(Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7)));
        userDO.setLastLoginIp(ip);
        userDO.setGmtLastLogin(now);
        userDO.setGmtUpdate(now);
        userDO.setGmtCreate(now);
        userDO.setLoginType(UserLoginType.REGISTER.getCode());
        userMapper.insert(userDO);
        //返回用户DTO
        cacheComponent.del(VERIFY_CODE_PREFIX + phone);
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bindPhone(String phone, String password, String verifyCode, Long userId) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        Integer count = userMapper.selectCount(
                new EntityWrapper<SpuUserDO>()
                        .eq("phone", phone));
        if (count > 0) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_HAS_EXISTED);
        }
        //3.校验成功，绑定手机
        SpuUserDO updateUserDO = new SpuUserDO();
        updateUserDO.setId(userId);
        updateUserDO.setPhone(phone);
        updateUserDO.setGmtUpdate(new Date());
        if (userMapper.updateById(updateUserDO) > 0) {
            cacheComponent.del(VERIFY_CODE_PREFIX + phone);
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String resetPassword(String phone, String password, String verifyCode) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        List<SpuUserDO> targetUserList = userMapper.selectList(
                new EntityWrapper<SpuUserDO>()
                        .eq("phone", phone));
        if (CollectionUtils.isEmpty(targetUserList)) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_NOT_EXIST);
        }
        Long id = targetUserList.get(0).getId();
        //3.校验成功，重置密码
        SpuUserDO updateUserDO = new SpuUserDO();
        updateUserDO.setId(id);
        updateUserDO.setPassword(Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7)));
        updateUserDO.setGmtUpdate(new Date());
        if (userMapper.updateById(updateUserDO) > 0) {
            cacheComponent.del(VERIFY_CODE_PREFIX + phone);
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    /**
     * 验证码抽取校验
     *
     * @param phone
     * @param verifyCode
     * @throws ServiceException
     */
    private void checkVerifyCode(String phone, String verifyCode) throws ServiceException {
        String raw = cacheComponent.getRaw(VERIFY_CODE_PREFIX + phone);
        if (StringUtils.isEmpty(raw)) {
            throw new AppServiceException(ExceptionDefinition.USER_VERIFY_CODE_NOT_EXIST);
        }
        if (!raw.equals(verifyCode)) {
            throw new AppServiceException(ExceptionDefinition.USER_VERIFY_CODE_NOT_CORRECT);
        }
    }


    @Override
    @Transactional
    public SpuUserDTO loginByVerifyCode(String phone, String verifyCode, Integer loginType, String raw, String ip, String openId) throws ServiceException {
        logger.info("我进来了=loginTyype=" + loginType + ">>raw==" + raw);
        logger.info("我进来了=phone=" + phone + ">>verifyCode==" + verifyCode);
        logger.info("我进来了=loginType=" + loginType + ">>openId==" + openId);

        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        Integer count = userMapper.selectCount(
                new EntityWrapper<SpuUserDO>()
                        .eq("phone", phone));
        if (count <= 0) {
            //3.校验成功，注册用户
            Date now = new Date();
            SpuUserDO userDO = new SpuUserDO();
            userDO.setPhone(phone);
            userDO.setPassword(Md5Crypt.md5Crypt(phone.getBytes(), "$1$" + phone.substring(0, 7)));
            userDO.setLastLoginIp(ip);
            userDO.setGmtLastLogin(now);
            userDO.setGmtUpdate(now);
            userDO.setGmtCreate(now);
            userDO.setNickname(phone.substring(0, 7));
            userDO.setOpenId(openId);
            userDO.setLoginType(loginType);
            userMapper.insert(userDO);
            //返回用户DTO
            cacheComponent.del(VERIFY_CODE_PREFIX + phone);
        }
        SpuUserDTO userDTO = userMapper.loginByPhone(phone);
        //检查帐号是否已经冻结
        if (userDTO.getStatus() == 0) {
            throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
        }
        /*if (!StringUtils.isEmpty(raw) && UserLoginType.contains(loginType)) {
            if (loginType == UserLoginType.MP_WEIXIN.getCode()) {
                try {
                    System.out.println("我进来了=loginType="+loginType+">>loginType=="+raw);
                    JSONObject thirdPartJsonObject = JSONObject.parseObject(raw);
                    String code = thirdPartJsonObject.getString("code");
                    String body = okHttpClient.newCall(new Request.Builder()
                            .url("https://api.weixin.qq.com/sns/jscode2session?appid=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxMiNiAppid : wxAppAppid) +
                                    "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxMiNiSecret : wxAppSecret) +
                                    "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
                    JSONObject jsonObject = JSONObject.parseObject(body);
                    Integer errcode = jsonObject.getInteger("errcode");
                    if (errcode == null || errcode == 0) {
                        String miniOpenId = jsonObject.getString("openid");
                        //将此次登录的openId，暂且放入user的域里面，支付的时候会用到
                        userDTO.setLoginType(loginType);
                        userDTO.setOpenId(miniOpenId);
                    }
                } catch (Exception e) {
                    logger.error("[微信第三方登录] 异常", e);
                    throw new ThirdPartServiceException(ExceptionDefinition.THIRD_PART_SERVICE_EXCEPTION.getMsg(), ExceptionDefinition.THIRD_PART_SERVICE_EXCEPTION.getCode());
                }
            }
        }*/
        String accessToken = GeneratorUtil.genSessionId();
        userDTO.setAccessToken(accessToken);
        addUserInfo(userDTO);
        //放入SESSION专用Redis数据源中
        userRedisTemplate.opsForValue().set(Const.SPU_USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
        SpuUserDO userUpdateDO = new SpuUserDO();
        userUpdateDO.setId(userDTO.getId());
        userUpdateDO.setGmtLastLogin(new Date());
        userUpdateDO.setLastLoginIp(ip);
        userMapper.updateById(userUpdateDO);
        return userDTO;
    }

    @Override
    @Transactional
    public SpuUserDTO login(String phone, String password, Integer loginType, String raw, String ip) throws ServiceException {
        String cryptPassword = Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7));
        SpuUserDTO userDTO = userMapper.login(phone, cryptPassword);
        if (userDTO == null) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_OR_PASSWORD_NOT_CORRECT);
        }
        //检查帐号是否已经冻结
        if (userDTO.getStatus() == 0) {
            throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
        }

        String accessToken = GeneratorUtil.genSessionId();
        userDTO.setAccessToken(accessToken);
        addUserInfo(userDTO);
        //放入SESSION专用Redis数据源中
        userRedisTemplate.opsForValue().set(Const.SPU_USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
        SpuUserDO userUpdateDO = new SpuUserDO();
        userUpdateDO.setId(userDTO.getId());
        userUpdateDO.setGmtLastLogin(new Date());
        userUpdateDO.setLastLoginIp(ip);
        userMapper.updateById(userUpdateDO);
        return userDTO;
    }

    @Override
    public String logout(String accessToken, Long userId) throws ServiceException {
        userRedisTemplate.delete(accessToken);
        return "ok";
    }


    @Override
    public String syncUserInfo(String nickName, String nickname, String avatarUrl, Integer gender, Long birthday, String accessToken, Long userId) throws ServiceException {
        SpuUserDO updateUserDO = new SpuUserDO();
        updateUserDO.setId(userId);
        updateUserDO.setNickname(StringUtils.isEmpty(nickName) ? nickname : nickName);
        updateUserDO.setAvatarUrl(avatarUrl);
        updateUserDO.setGender(gender);
        updateUserDO.setGmtUpdate(new Date());
        if (birthday != null)
            updateUserDO.setBirthday(new Date(birthday));
        if (userMapper.updateById(updateUserDO) > 0) {
            //更新SESSION缓存
            SpuUserDTO user = SessionUtil.getSpuUser();
            if (!StringUtils.isEmpty(nickName)) {
                user.setNickname(nickName);
            }
            if (!StringUtils.isEmpty(avatarUrl)) {
                user.setAvatarUrl(avatarUrl);
            }
            if (birthday != null) {
                user.setBirthday(new Date(birthday));
            }
            if (gender != null) {
                user.setGender(gender);
            }
            userRedisTemplate.opsForValue().set(Const.SPU_USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(user));
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    @Transactional
    public SpuUserDTO testLogin() throws ServiceException {
        SpuUserDTO userDTO = userMapper.getUserById(1L);
        if (userDTO == null) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_OR_PASSWORD_NOT_CORRECT);
        }
        //检查帐号是否已经冻结
        String accessToken = GeneratorUtil.genSessionId();
        addUserInfo(userDTO);
        //放入SESSION专用Redis数据源中
        userRedisTemplate.opsForValue().set(Const.SPU_USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
        userDTO.setAccessToken(accessToken);
        return userDTO;
    }

    void addUserInfo(SpuUserDTO userDTO) {
        //==增加店铺查询放入缓存
        UserShopDO userShopDO = userShopMapper.getUserShopByUserId(userDTO.getId());
        if (userShopDO != null)
            userDTO.setUserShopDO(userShopDO);
    }


    @Override
    public JSONObject getOpenId(String code) throws ServiceException {
        try {
            //H5 微信公众号网页登录
            String json = okHttpClient.newCall(
                    new Request.Builder().url("https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                            + wxH5Appid + "&secret=" + wxH5Secret + "&code=" + code + "&grant_type=authorization_code").build()).execute().body().string();
            JSONObject jsonObject = JSONObject.parseObject(json);

            return jsonObject;
        } catch (Exception e) {
            logger.error("获取openId异常", e);
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_LOGIN_FAILED);
        }
    }

}
